/************************************************************************/
/*    Author: Scott Bevin & Christopher Pepper                                               */
/************************************************************************/


#pragma once

#include <stdio.h> 
#include <stdlib.h> 
#include <windows.h> 

#include <vector>
#include <string>
#include <sstream>

#include <Ogre.h>

#include "../XFrameworkTypedefsAndDefines.h"
#include "Random.h"
#include <assert.h>

namespace XF
{
	namespace Utilities
	{
		template<class T>
		inline std::string ToString(const T value)
		{
			std::stringstream sstr;
			sstr << value;
			return sstr.str();
		}

		inline std::string ToString(const XF::Vector3 &value)
		{
			return "{" + ToString<float>(value.x) + ", " + ToString<float>(value.y) + ", " + ToString<float>(value.z) + "}";
		}

		inline std::string ToString(const XF::Vector2 &value)
		{
			return "{" + ToString<float>(value.x) + ", " + ToString<float>(value.y) + "}";
		}

		inline std::string ToString(const bool &value)
		{
			return (value ? "true" : "false");
		}

		inline char ToHigher(char letter) //chris
		{
			if(letter > 97)
			{
				letter -= 32;
			}
			return letter;
		}

		inline std::string ToHigher(std::string str) //chris
		{
			for(int i = 0; i < (int)str.size(); ++i)
			{
				char letter = str[i];
				if(letter > 97)
				{
					letter -= 32;
				}
				str[i] = letter;
			}
			return str;
		}

		inline std::string CreateRandomString()
		{
			return XF::Utilities::ToString(XF::Random::NextInt(10000, 99999)) + 
				XF::Utilities::ToString(XF::Random::NextInt(10000, 99999)) + 
				XF::Utilities::ToString(XF::Random::NextInt(10000, 99999));
		}

		template <class T>
		inline bool FromString(T& t, 
						 const std::string& s, 
						 std::ios_base& (*f)(std::ios_base&) = std::dec)
		{
		  std::istringstream iss(s);
		  return !(iss >> f >> t).fail();
		}		
		
		inline void TokenizeString(const std::string& str,
					std::vector<std::string>& tokens,
					const std::string& delimiters = " ")
		{
			// Skip delimiters at beginning.
			std::string::size_type lastPos = str.find_first_not_of(delimiters, 0);
			// Find first "non-delimiter".
			std::string::size_type pos     = str.find_first_of(delimiters, lastPos);

			while (std::string::npos != pos || std::string::npos != lastPos)
			{
				// Found a token, add it to the vector.
				tokens.push_back(str.substr(lastPos, pos - lastPos));
				// Skip delimiters.  Note the "not_of"
				lastPos = str.find_first_not_of(delimiters, pos);
				// Find next "non-delimiter"
				pos = str.find_first_of(delimiters, lastPos);
			}
		}

		inline XF::Vector3 FromString(std::string value)
		{
			XF::Vector3 outValue;

			// remove { }
			std::string stripped = value.substr(1, value.size() - 2);

			std::vector<std::string> values;
			TokenizeString(stripped, values, ", ");

			if(values.size() < 3)
			{
				return Vector3::ZERO;
			}
			
			FromString<float>(outValue.x, values[0], std::dec);
			FromString<float>(outValue.y, values[1], std::dec);
			FromString<float>(outValue.z, values[2], std::dec);

			return outValue;
		}

		inline bool StringContains(const std::string& str, const std::string& value)
		{
			return (str.find(value) != str.npos);
		}

		inline bool StringOnlyContains(const std::string& str, char var)
		{
			for(int i = 0; i < (int)str.size(); ++i)
			{
				if(str[i] != var) return false;
			}
			return true;
		}
		inline std::string ToLower(std::string val)
		{
			std::string lowerVal = val;
			int i = 0;
			while (lowerVal[i])
			{
				char c=lowerVal[i];
				lowerVal[i] = (tolower(c));
				i++;
			}
			return lowerVal;
		}
		inline std::string JoinStrings(std::vector<std::string> &strVec) //I edited this function a little (chris)
		{
			std::string outStr;

			for(int i = 0; i < (int)strVec.size(); ++i)
			{
				if(i > 0)
					outStr +=  " " + strVec[i];//chris (so that we dont have trailing space i added an else and switched this round)
				else
					outStr += strVec[i]; 
			}

			return outStr;
		}

		//************************************
		// Method:    RemoveCharacters
		// FullName:  XF::Utilities::RemoveCharacters
		// Access:    public 
		// Returns:   std::string
		// Qualifier: /*chris */
		// Parameter: std::string theString
		// Parameter: char * ofChars
		// Removes any occurrence of the chars in the char array
		//************************************
		inline std::string RemoveCharacters(std::string theString, char* ofChars) //chris
		{
			std::string newString;
			for(int i = 0; i < (int)theString.length(); ++i)
			{
				bool add = true;

				for(int j = 0; ofChars[j] != NULL; ++j)
				{
					if(ofChars[j] == theString[i])
					{
						add = false;
					}
				}

				if(add)
				{
					newString+=theString[i];
				}
			}
			return newString;
		}
		//************************************
		// Method:    TrimStart
		// FullName:  XF::Utilities::TrimStart
		// Access:    public 
		// Returns:   std::string
		// Qualifier: /*chris */
		// Parameter: std::string theString
		// Parameter: char * ofChars
		//Removes any occurrence of the chars in the char array
		//up to the first character that is not that e.g. TrimStart("   z  Fredz  ", "z ")
		//would return "Fredz  "
		//************************************
		inline std::string TrimStart(std::string theString, char* ofChars) //chris
		{
			int i = 0;
			int count = theString.length();
			bool carryOnSearching = true;
			for(i; i < count && carryOnSearching ; ++i)
			{

				int j = 0;
				int numTrue = 0;
				for(j = 0; j < ofChars[j] != NULL; ++j)
				{
					numTrue += (int)(ofChars[j] == theString[i]);
				}
				carryOnSearching =  numTrue >= 1;


			}

			if(i > 0)
				return theString.substr(i-1,count);	
			else
				return theString;

		}
		//************************************
		// Method:    TrimEnd
		// FullName:  XF::Utilities::TrimEnd
		// Access:    public 
		// Returns:   std::string
		// Qualifier: /*chris */
		// Parameter: std::string theString
		// Parameter: char * ofChars
		//Removes any occurrence of the chars in the char array
		//backwards up to the first character that is not that e.g. TrimEnd("   z  Fredz  ", "z ")
		//would return "   z  Fred"
		//************************************
		inline std::string TrimEnd(std::string theString, char* ofChars) //chris
		{

			int count = theString.length();
			int i = count-1;
			bool carryOnSearching = true;
			for(i ; i >= 0 && carryOnSearching ; --i)
			{

				int j = 0;
				int numTrue = 0;
				for(j = 0; j < ofChars[j] != NULL; ++j)
				{
					numTrue += (int)(ofChars[j] == theString[i]);
				}
				carryOnSearching =  numTrue >= 1;


			}


			if(i > 0)
				return theString.substr(0,i+2);	
			else
				return theString;
		}
		//************************************
		// Method:    Trim
		// FullName:  XF::Utilities::Trim
		// Access:    public 
		// Returns:   std::string
		// Qualifier: /*chris */
		// Parameter: std::string theString
		//Performs TrimStart and then TrimEnd, so 
		// Trim("  z   X d  Frederick d x z", "zx d") wold return
		//"Frederick"
		//************************************
		inline std::string Trim(std::string theString, char* ofChars) //chris
		{
			std::string newString = TrimStart(theString, ofChars);
			newString = TrimEnd(newString, ofChars);

			return newString;
		}

		inline std::vector<std::string> GetAllFilesInDirectory(std::string directory, std::string extention = ".*")
			{
				std::vector<std::string> retVec;
	
				WIN32_FIND_DATA info;
	
				char oldDir[MAX_PATH] = {""};
				GetCurrentDirectory(MAX_PATH, oldDir);
	
				std::string dir = std::string(oldDir) + "/" + directory + "/ *" + extention;
	
				HANDLE fileHandle = FindFirstFile(dir.c_str(), &info);
	
				if (fileHandle != INVALID_HANDLE_VALUE)
				{
					do
					{
						if (!(strcmp(info.cFileName, ".") == 0 || strcmp(info.cFileName, "..") == 0))
						{
							retVec.push_back(info.cFileName);
						}
	
					} while (FindNextFile(fileHandle, &info));
	
					FindClose(fileHandle);
				}
	
				return retVec;
			}
	
		//************************************
		// Method:    RemoveExtensionAndFolders
		// FullName:  XF::Utilities::RemoveExtensionAndFolders
		// Access:    public 
		// Returns:   std::string
		// Qualifier:
		// Parameter: std::string file
		// Parameter: std::string folderSep
		//strips a file path of its extension and folders, you can specify folder sep as either \\ or / default is \\
		//************************************
		inline std::string RemoveExtensionAndFolders(std::string file,std::string folderSep = "\\")
		{
			std::string::size_type folderPos = file.find_last_of(folderSep);
			std::string withoutFolders = file.substr(folderPos+1,file.size());
			std::string::size_type dotPos = withoutFolders.find_last_of(".");			
			std::string withoutExtension = withoutFolders.substr(0,dotPos);
			//std::string withoutExtension = file.substr(dotPos);		
			return withoutExtension;		
		}

		//-------------------------------------------- Chris I added this functions from my routines may prove useful
		//************************************
		// Method:    GetFileNamesInDirectory
		// FullName:  XF::Utilities::GetFileNamesInDirectory
		// Access:    public 
		// Returns:   bool
		// Qualifier:
		// Parameter: std::string directory
		// Parameter: std::vector<std::string> & files
		// Parameter: bool recurse
		//gets all the files in directory, so the same as yours but with recurse and error checking
		//************************************
		inline bool GetFileNamesInDirectory(std::string directory, std::vector<std::string>&files, bool recurse = true, bool includeFoldersInPath = true)
		{
			HANDLE          hFile;                   // Handle to file
			WIN32_FIND_DATA FileInformation;         // File information


			std::string directoryPath = directory + "\\*.*";

			hFile = ::FindFirstFile(directoryPath.c_str(), &FileInformation);

			if(hFile != INVALID_HANDLE_VALUE)
			{		
				do
				{
					//check first character of string is not '.'
					std::string aFile  = (char*)FileInformation.cFileName;
					if(FileInformation.cFileName[0] != '.'&& aFile.find("_svn") == std::string::npos)
					{
						
						if( aFile.find('.') == std::string::npos) //is a folder (or a file with no extension)
						{
							if(recurse)
							{	
								if(!includeFoldersInPath)
									GetFileNamesInDirectory(directory+"\\"+aFile, files );
								else
								{
									std::vector<std::string> temp;
									GetFileNamesInDirectory(directory+"\\"+aFile, temp, true);
									for(int i = 0; i < (int)temp.size(); ++i)
									{
										std::string fileWithFolder = aFile+"\\"+temp[i];
										files.push_back(fileWithFolder);
									}
								}
							}
						}
						else
						{
							files.push_back(aFile);
						}

					}
				}while(::FindNextFile(hFile, &FileInformation) == TRUE);
				return true;
			}
			return false;

		}

		//---------------------------------------------------------
		//************************************
		// Method:    GetFileNamesWithExtension
		// FullName:  XF::Utilities::GetFileNamesWithExtension
		// Access:    public 
		// Returns:   bool
		// Qualifier:
		// Parameter: std::string extension:	the extension you want
		// Parameter: std::string directory: in the directory you want
		// Parameter: std::vector<std::string> & files: the vector you want the files in
		// Parameter: bool recurse: if you want the function to into folders (and folders in those folders) that in finds in the directory
		// Parameter: bool includeFoldersInPath: if you are recursing do you want the folder name to be included in the files list e.g file dog is in folder fred, if true file list will have fred/dog, if false just dog
		//************************************
		inline bool GetFileNamesWithExtension(std::string extension, std::string directory, std::vector<std::string>&files, bool recurse=true, bool includeFoldersInPath=false)
		{
			HANDLE          hFile;                   // Handle to file
			WIN32_FIND_DATA FileInformation;         // File information


			std::string directoryPath = directory + "\\*.*";

			hFile = ::FindFirstFile(directoryPath.c_str(), &FileInformation);

			if(hFile != INVALID_HANDLE_VALUE)
			{		
				do
				{
					std::string aFile  = (char*)FileInformation.cFileName;
					//check first character of string is not '.'
					if(FileInformation.cFileName[0] != '.' && aFile.find("_svn") == std::string::npos) 
					{
						
						if( aFile.find('.') == std::string::npos) //is a folder (or a file with no extension)
						{
							if(recurse)
							{
								if(!includeFoldersInPath)
									GetFileNamesWithExtension(extension,directory+"\\"+aFile, files, true,false);
								else
								{
									std::vector<std::string> temp;
									GetFileNamesWithExtension(extension,directory+"\\"+aFile, temp, true, true);
									for(int i = 0; i < (int)temp.size(); ++i)
									{
										std::string fileWithFolder = aFile+"\\"+temp[i];
										files.push_back(fileWithFolder);
									}
								}
							}
						}
						else
						{
							if( aFile.find(extension) != std::string::npos)
								files.push_back(aFile);
						}

					}
				}while(::FindNextFile(hFile, &FileInformation) == TRUE);
				return true;
			}
			return false;

		}
		//************************************
		// Method:    RemoveAllStringsThatContain
		// FullName:  XF::Utilities::RemoveAllStringsThatContain
		// Access:    public 
		// Returns:   std::vector<std::string>
		// Qualifier:
		// Parameter: std::vector<std::string> & unwantedStrings
		// Parameter: std::vector<std::string> & stringList
		//does what it says, returns the result
		//************************************
		inline std::vector<std::string> RemoveAllStringsThatContain(std::vector<std::string>&unwantedStrings,std::vector<std::string>&stringList)
		{
	
			bool add = false;
			std::vector<std::string> newList;
			for(int i = 0; i < (int)stringList.size(); ++i)
			{
				std::string aString = stringList[i];
				for(int i = 0; i < (int)unwantedStrings.size(); ++i)
				{
					if( aString.find(unwantedStrings[i]) != std::string::npos)
					{
						add = false;
						i = newList.size();
					}
				}

				if(add)
				{
					newList.push_back(aString);
				}
			}
		

			return newList;
		}
		//************************************
		// Method:    FilterExtensions
		// FullName:  XF::Utilities::FilterExtensions
		// Access:    public 
		// Returns:   bool
		// Qualifier:
		// Parameter: std::vector<std::string> & validExtensions (the extensions you wish to HAVE)
		// Parameter: std::vector<std::string> & fileList	(the file list)
		// Parameter: std::vector<std::string> & newFileList (this will return the new file list of files)
		// Parameter: std::vector<std::string> * filesExcluded (if you want to know which files were excluded send another vector by ref &)
		//************************************
		inline bool FilterExtensions(std::vector<std::string>&validExtensions, std::vector<std::string>&fileList, std::vector<std::string>&newFileList , std::vector<std::string>*filesExcluded = NULL)
		{
			if(validExtensions.empty())return false;
			if(fileList.empty())return false;

			bool add = false;

			for(int i = 0; i < (int)fileList.size(); ++i)
			{
				std::string aFile = fileList[i];
				for(int i = 0; i < (int)validExtensions.size(); ++i)
				{
					if( aFile.find(validExtensions[i]) != std::string::npos)
					{
						add = true;
						i = validExtensions.size();
					}
				}

				if(add)
				{
					newFileList.push_back(aFile);
				}
				else if(filesExcluded)
				{
					(*filesExcluded).push_back(aFile);
				}
			}
			if(newFileList.empty())return false;

			return true;

		}
		//---------------------------------------------------------
		//************************************
		// Method:    MillisecondsToSecondsMinutes
		// FullName:  XF::Utilities::MillisecondsToSecondsMinutes
		// Access:    public 
		// Returns:   void
		// Qualifier:
		// Parameter: float ms
		// Parameter: int & minutes
		// Parameter: int & seconds
		// Parameter: int & newMs
		//converts millieseconds into seconds, minutes
		//************************************
		inline void MillisecondsToSecondsMinutes( float ms, int& minutes, int &seconds, int &newMs )
		{
			seconds = ms;
			minutes = ms/60;	
			float millieseconds = ms - seconds;
			newMs = (int)(millieseconds*100);
			seconds = seconds - (60*minutes);
		}

		static void Assert(bool condition, const std::string &text)
		{
			assert(condition && text.c_str());
		}
	}

}